﻿using QntPlatform.Db;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Unicode;

namespace QntPlatform
{

    public static class LogExt
    {
        public static void InfoByOrder(this ILog log, object msg, IExchange exchange, SideDirection sideDirection, decimal amount, decimal price, string actId, string pActId, string exteOrderId, LogInfo info = null)
        {
            //var newInfo= LogInfo.CreateOrderLog(exchange, sideDirection, amount, price, actId, pActId, exteOrderId, info);
            info = CreateLogInfo(exchange, sideDirection, amount, price, actId, pActId, exteOrderId, info);
            log.Info(msg, msg3: info);
        }

        private static LogInfo CreateLogInfo(IExchange exchange, SideDirection sideDirection, decimal amount, decimal price, string actId, string pActId, string exteOrderId, LogInfo info)
        {
            if (info == null)
            {
                info = new LogInfo();
            }
            info.LogType = "OrderCreate";
            info.Info1 = $"{exchange.Name}_{exchange.Currency},{sideDirection},{amount},{price}";
            if (!string.IsNullOrWhiteSpace(actId))
                info.ActId = actId;
            if (!string.IsNullOrWhiteSpace(pActId))
            {
                info.ParentActId = pActId;
            }
            info.ExteOrderId = exteOrderId;
            return info;
        }

        public static void ErrorByOrder(this ILog log, object msg, IExchange exchange, SideDirection sideDirection, decimal amount, decimal price, string actId, string pActId, LogInfo info = null)
        {

            info = CreateLogInfo(exchange, sideDirection, amount, price, actId, pActId, null, info);
            log.Error(msg, msg3: info);
        }
    }
    public interface ILog
    {
        void Debug(object msg, object msg2 = null, object msg3 = null, [CallerMemberName] string callName = null);
        void Error(object msg, object msg2 = null, object msg3 = null, [CallerMemberName] string callName = null);
        void Info(object msg, object msg2 = null, object msg3 = null, [CallerMemberName] string callName = null);
        void Warn(object msg, object msg2 = null, object msg3 = null, [CallerMemberName] string callName = null);
    }

    public class Log : ILog
    {
        public Log() {
            if (!File.Exists(Path))
            {
                Directory.CreateDirectory(System.IO.Path.GetDirectoryName(Path));
            }
        }
        //static Log defaultLog;
        public static ILog Default()
        {
            return LogManager.GetLogger("default");
            // if (defaultLog == null)
            // {
            //     defaultLog = new Log();
            //     defaultLog.Name = "default";
            // }
            // return defaultLog;
        }
        public static ILog GetLogger(object name)
        {
            return LogManager.GetLogger(name);
        }

        public string Name { get; set; }
        protected IStrParse strParse = new LiteParse();
        public readonly string Path = "wwwroot/Log.txt";
        protected virtual void WriteLog(TraceLevel level, object msg, object msg2 = null, object msg3 = null, string callName = null)
        {
            try
            {
                try
                {
                    if (msg != null && !(msg is string or Exception))
                    {
                        msg = strParse.ToText(msg);
                    }
                    if (msg2 != null && !(msg2 is string or Exception))
                    {
                        msg2 = strParse.ToText(msg2);
                    }
                    if (msg3 != null && !(msg3 is string or Exception))
                    {
                        msg3 = strParse.ToText(msg3);
                    }

                }
                catch (Exception ex2)
                {
                    ex2 = ex2.Demystify();
                    //EnhancedStackTrace.Current();
                    lock (Path)
                    {
                        File.AppendAllText(Path, DateTime.Now + " 日志消息对象序列化异常：" + ex2.ToString());
                        return;
                    }
                }
                var msgArr = new[] { msg,msg2,msg3};
                for (int i = 0; i < msgArr.Length; i++)
                {
                    var msgEx = msgArr[i] as Exception;
                    if (msgEx!=null)
                    {
                        msgArr[i] = msgEx.ToStringDemystified();
                    }
                }
                var str = $"{DateTime.Now } {Name}:{level}:{callName}:{msgArr[0]},{msgArr[1]},{msgArr[2]} \r\n";
                Console.Write(str);
                lock (Path)
                {
                    File.AppendAllText(Path, str);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("-----------------------------------------");
                Console.WriteLine("日志写入异常:" + ex);

            }

        }
        //CallerFilePath,CallerLineNumber
        public void Debug(object msg, object msg2 = null, object msg3 = null, [CallerMemberName] string callName = null)
        {
            WriteLog(TraceLevel.Verbose, msg, msg2, msg3, callName);
        }
        public void Info(object msg, object msg2 = null, object msg3 = null, [CallerMemberName] string callName = null)
        {
            WriteLog(TraceLevel.Info, msg, msg2, msg3, callName);
        }
        public void Warn(object msg, object msg2 = null, object msg3 = null, [CallerMemberName] string callName = null)
        {
            WriteLog(TraceLevel.Warning, msg, msg2, msg3, callName);
        }
        public void Error(object msg, object msg2 = null, object msg3 = null, [CallerMemberName] string callName = null)
        {
            WriteLog(TraceLevel.Error, msg, msg2, msg3, callName);
        }

        public void d(object msg, object msg2 = null, object msg3 = null, [CallerMemberName] string callName = null) { Debug(msg, msg2, msg3, callName); }
        public void i(object msg, object msg2 = null, object msg3 = null, [CallerMemberName] string callName = null) { Info(msg, msg2, msg3, callName); }
        public void w(object msg, object msg2 = null, object msg3 = null, [CallerMemberName] string callName = null) { Warn(msg, msg2, msg3, callName); }
        public void e(object msg, object msg2 = null, object msg3 = null, [CallerMemberName] string callName = null) { Error(msg, msg2, msg3, callName); }
    }
    public class RobotLog : Log
    {
        public long RobotId { get; set; }
        public string LogType { get; set; }
        public RobotLog(long robotId)
        {
            RobotId = robotId;
        }
        public static event EventHandler<LogInfo> LogWriteEvent;
        protected override void WriteLog(TraceLevel level, object msg, object msg2 = null, object msg3 = null, string callName = null)
        {
            LogInfo msg3Info = null;
            try
            {
                if (msg != null && !(msg is string or Exception))
                {
                    msg = strParse.ToText(msg);
                }
                if (msg2 != null && !(msg2 is string or Exception))
                {
                    msg2 = strParse.ToText(msg2);
                }

                if (msg3 != null && msg3 is LogInfo)
                {
                    msg3Info = (LogInfo)msg3;
                }
                else if (msg3 != null && !(msg3 is string or Exception))
                {
                    msg3 = strParse.ToText(msg3);
                }
            }
            catch (Exception ex)
            {
                //Console.WriteLine($"{DateTime.Now} :robot日志消息序列化异常:{RobotId},{msg},{msg2},{msg3},{ex}");
                Log.Default().Error($"robot日志消息序列化异常:{RobotId},{msg},{msg2},{msg3},{ex}");
            }
            try
            {
                var info = new LogInfo();
                info.LogLevel = (int)level;
                info.OwnerId = RobotId;
                info.LogType = LogType;
                info.Time = DateTime.Now;
                info.Info1 = Name;
                var msgArr = new[] { msg, msg2, msg3 };
                for (int i = 0; i < msgArr.Length; i++)
                {
                    var msgEx = msgArr[i] as Exception;
                    if (msgEx != null)
                    {
                        msgArr[i] = msgEx.ToStringDemystified();
                    }
                }
                if (msg3Info != null)
                {
                    info.LogType = msg3Info.LogType ?? info.LogType;
                    info.Info1 = msg3Info.Info1 ?? info.Info1;
                    info.Info2 = msg3Info.Info2;
                    info.Info3 = msg3Info.Info3;
                    info.Msg = $"{Name}:{callName}:{msgArr[0]},{msgArr[1]}";
                }
                else
                {
                    info.Msg = $"{Name}:{callName}:{msgArr[0]},{msgArr[1]},{msgArr[2]}";
                }
                //var str = $"{DateTime.Now} _{RobotId}_{Name}:{level}:{callName}:{msg},{msg2},{msg3}";
                // Console.WriteLine(str);
                LogStore.Insert(new[] { info });
                LogWriteEvent?.Invoke(this, info);
                base.WriteLog(level, info.Msg, callName: callName);
            }
            catch (Exception ex)
            {
                ex = ex.Demystify();
                Console.WriteLine(DateTime.Now + ": RobotId:" + RobotId + "_LogStore异常:" + ex);
                Log.Default().Error("RobotId:" + RobotId + "_LogStore异常:" + ex);
            }
        }

    }
    public class LogManager
    {
        static IDictionary<string, ILog> dic = new Dictionary<string, ILog>();
        public static ILog GetLogger(object name = null)
        {

            var logName = "";
            if (name == null)
                logName = "default";
            else if (name is IConvertible)
                logName = name + "";
            else if (name is Type)
                logName = (name as Type).Name;
            else
                logName = name.GetType().Name;
            if (dic.TryGetValue(logName, out var val))
                return val;
            else
            {
                var log = new Log();
                log.Name = logName;
                dic.Add(log.Name, log);
                return log;
            }
        }
    }

    public class LiteParse : IStrParse
    {
        JsonSerializerOptions options = new JsonSerializerOptions()
        {
            NumberHandling = JsonNumberHandling.AllowReadingFromString,
            ReadCommentHandling = JsonCommentHandling.Skip,
            AllowTrailingCommas = true,
            IncludeFields = true,
            Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping // System.Text.Encodings.Web.JavaScriptEncoder.Create (UnicodeRanges.All)
        };
        public LiteParse()
        {
            options.Converters.Add(new ObjLiteConverter() { IsLiteObj = (p1, p2) => p1 > 3 });
        }
        public T Parse<T>(string txt)
        {
            return JsonSerializer.Deserialize<T>(txt);
        }

        public string ToText<T>(T obj)
        {
            return JsonSerializer.Serialize(obj, options);
        }

        public class ObjLiteConverter : JsonConverter<Object>
        {
            public Func<int, object, bool> IsLiteObj { get; set; }

            public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
            {
                using (JsonDocument document = JsonDocument.ParseValue(ref reader))
                {
                    return document.RootElement.Clone();
                }
            }

            public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
            {
                //Console.WriteLine(writer.CurrentDepth);
                if (IsLiteObj != null && IsLiteObj(writer.CurrentDepth, value) && value is not IConvertible)
                {
                    if (value is not IEnumerable)
                    {
                        writer.WriteStartObject();
                        writer.WriteEndObject();
                    }
                    else
                    if (value.GetType().IsArray)
                    {
                        writer.WriteStartArray();
                        writer.WriteEndArray();
                    }
                    else
                        writer.WriteNullValue();
                    // IDictionary<string,string>

                }
                else
                    JsonSerializer.Serialize(writer, value, options);

            }
        }
    }
}